/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */
import os from 'os';
import path from 'path';
import process from 'process';

import { FileSet, hvigorCore } from '@ohos/hvigor';
import fse from 'fs-extra';

import { BuildArtifactConst } from '../const/build-directory-const.js';
import { CommonConst } from '../const/common-const.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';
import { TaskNames } from './common/task-names.js';
import { TargetTaskService } from './service/target-task-service.js';
import { OhosHapTask } from './task/ohos-hap-task.js';

/**
 * 将打hap/hsp/hsp/app等构建过程中的debug symbol收集到产物指定目录symbol下
 */
export class CollectDebugSymbol extends OhosHapTask {
  protected _log: OhosLogger = OhosLogger.getLogger(CollectDebugSymbol.name);

  private readonly _taskService: TargetTaskService;
  private readonly RELEASE: string = 'release';
  private readonly DEBUG: string = 'debug';
  private readonly debugSymbolPath: string;
  private readonly modulePath: string;
  private isRelease: boolean;
  private shouldCollectDebugSymbol: boolean;

  constructor(taskService: TargetTaskService) {
    super(taskService, TaskNames.Task.COLLECT_DEBUG_SYMBOL);
    this._taskService = taskService;
    /**
    读取ohos.collect.debugSymbol的配置开关；如果没有配置，则release模式下默认开启，debug模式下默认关闭
     */
    this.isRelease = !taskService.getBuildOption()?.debuggable;
    this.shouldCollectDebugSymbol = hvigorCore.getParameter().getProperty(CommonConst.USE_COLLECT_DEBUG_SYMBOL) ?? this.isRelease;
    this.modulePath = this.pathInfo.getModulePath();
    this.debugSymbolPath = this.pathInfo.getSymbolsDir(this.modulePath);
  }

  /**
   * 检查增量输入输出
   * 输入检查所有要收集的文件的原路径
   * 输出检查整个输出文件夹
   */
  declareInputFiles(): FileSet {
    const fileSet = new FileSet();
    // 检查sourceMap
    if (fse.existsSync(this.pathInfo.getSourceMapOriginPath(this.targetService))) {
      fileSet.addEntry(this.pathInfo.getSourceMapOriginPath(this.targetService));
    }
    // 检查namecache.json
    if (fse.existsSync(this.pathInfo.getObfNameCachePath())) {
      fileSet.addEntry(this.pathInfo.getObfNameCachePath());
    }
    // 检查.so文件夹
    if (fse.existsSync(this.pathInfo.getIntermediatesProcessLibs())) {
      fileSet.addEntry(this.pathInfo.getIntermediatesProcessLibs(), { isDirectory: true });
    }

    return fileSet;
  }

  declareOutputFiles(): FileSet {
    if (!this.debugSymbolPath) {
      return new FileSet();
    }
    return new FileSet().addEntry(this.debugSymbolPath, { isDirectory: true });
  }
  /**
  检查配置是否发生改变
   */
  async beforeAlwaysAction() {
    this.isRelease = !this._taskService.getBuildOption()?.debuggable;
    this.shouldCollectDebugSymbol = hvigorCore.getParameter().getProperty(CommonConst.USE_COLLECT_DEBUG_SYMBOL) ?? this.isRelease;
  }

  /**
  根据shouldCollectDebugSymbol来判断是否应该执行收集任务
   */
  taskShouldDo(): boolean {
    return this.shouldCollectDebugSymbol;
  }
  protected doTaskAction(): void {
    // 创建产物路径
    fse.ensureDirSync(this.getDestinationPath());
    // sourceMap归档，优先从intermediate目录中读取
    if (fse.existsSync(this.pathInfo.getSourceMapIntermediatePath())) {
      const sourceMapPath = this.pathInfo.getSourceMapIntermediatePath();
      fse.copyFileSync(sourceMapPath, path.resolve(this.getDestinationPath(), `${this.getModuleName()}-${BuildArtifactConst.SOURCEMAPS_MAP}`));
    } else {
      const sourceMapPath = this.pathInfo.getSourceMapOriginPath(this.targetService);
      if (fse.existsSync(sourceMapPath)) {
        fse.copyFileSync(sourceMapPath, path.resolve(this.getDestinationPath(), `${this.getModuleName()}-${BuildArtifactConst.SOURCEMAPS_MAP}`));
      } else {
        this._log.debug(`${BuildArtifactConst.SOURCEMAPS_MAP} for module ${this.getModuleName()} does not exist`);
      }
    }

    // nameCached归档, release下才能开启混淆模式，har包产物路径与hap hsp包产物路径基本一致，只有一个文件夹名字不一样
    const nameCacheJsonPath = this.pathInfo.getObfNameCachePath();
    if (fse.existsSync(nameCacheJsonPath)) {
      fse.copyFileSync(nameCacheJsonPath, path.resolve(this.getDestinationPath(), `${this.getModuleName()}-${BuildArtifactConst.NAME_CACHE}`)); // 如果已存在会替换
    } else {
      this._log.debug(`${BuildArtifactConst.NAME_CACHE} for module ${this.getModuleName()} does not exists`);
    }

    // 归档未被strip的so，使用软连接
    this.createSymbolicLinkForSoFolder();
  }
  initTaskDepends() {}

  // tool functions below:
  /**
   * 返回输出目标路径，不存在就创建，保证输出不为空，否则会影响之后创建软连接
   * @private
   * @return destinationPath
   */
  private getDestinationPath(): string {
    // 检查并创建目标文件夹
    const destinationPath = this.isRelease ? path.resolve(this.debugSymbolPath, this.RELEASE) : path.resolve(this.debugSymbolPath, this.DEBUG);
    if (!fse.existsSync(destinationPath)) {
      fse.mkdirSync(destinationPath, { recursive: true });
    }
    return destinationPath;
  }

  /**
   * 建立so文件夹的软连接到目标文件夹,软连接的使用方法要考虑跨平台的兼容性
   * @private
   */
  private createSymbolicLinkForSoFolder() {
    const IS_WINDOWS = process.platform === 'win32' || os.type() === 'Windows_NT';
    const symlinkType = IS_WINDOWS ? 'junction' : 'dir';
    const unStrippedSoPath = this.pathInfo.getIntermediatesProcessLibs();
    if (fse.existsSync(unStrippedSoPath)) {
      // 读取 libs 文件夹下的内容,筛选出所有文件夹
      const folders = fse.readdirSync(unStrippedSoPath, { withFileTypes: true });
      const folderNames = folders.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
      // 不存在libs的话则打debug日志
      if (folderNames.length === 0) {
        this._log.debug(`There are no cpp libraries for module ${this.getModuleName()}`);
      }
      folderNames.forEach((folderName) => {
        const originLibsPath = path.resolve(unStrippedSoPath, folderName);
        const destinationLinkPath = path.resolve(this.getDestinationPath(), folderName);
        // 创建软连接时，应保证父路径存在，以及保证该文件夹不存在
        try {
          if (!fse.existsSync(destinationLinkPath)) {
            fse.symlinkSync(originLibsPath, destinationLinkPath, symlinkType);
          }
        } catch (err) {
          // 创建软连接失败，给出告警，警告包含node原生错误
          this._log.warn(`Create symbolic link for ${originLibsPath} failed. Reason: ${err}`);
        }
      });
    }
  }
  /**
  har的产物目录跟hap hsp的产物目录下的名称有所不同，需要进行区分
   */
  private getCompileArkFolderName(): string {
    return this.targetService.getModuleService().getModuleModel().isHarModule() ? 'HarCompileArkTS' : 'CompileArkTS';
  }

  private getModuleName() {
    return this.targetService.getModuleService().getModuleModel().getName();
  }
}
